#include <minix/drivers.h>
#include <minix/vtreefs.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "bencode.h"

#define FILE_LOCATION "/mnt/hgfs/minix_shared/as3/as3_t3/file.ben"

/** The struct which contains a rss channel */
typedef struct rsschannel {
	char *title;
	char *link;
	char *description;
	char *language;
	struct rssitem **items;
	int itemCount;
} rsschannel;

/** One item in a rss channel */
typedef struct rssitem {
    char* title;
    char* link;
    char* description;
    char* pubDate;
    char* guid;
} rssitem;

/** The rss channel read from the file */
static struct rsschannel *rss;

/** String used for storage when reading data from a file */
static char *data;

/** A list of all the inodes of the files. Not very usefull yet, but may be usefull for the next task */
static struct inode **files;

struct rssitem* parseItem(be_dict *dictionary){
	struct rssitem *item;
	int i;

	item = malloc(sizeof(struct rssitem));

	for (i = 0; dictionary[i].val; ++i) {
		if(strcmp(dictionary[i].key,"title")==0){
			item->title=dictionary[i].val->val.s;
		}else if(strcmp(dictionary[i].key,"link")==0){
			item->link=dictionary[i].val->val.s;
		}else if(strcmp(dictionary[i].key,"description")==0){
			item->description=dictionary[i].val->val.s;
		}else if(strcmp(dictionary[i].key,"pubDate")==0){
			item->pubDate=dictionary[i].val->val.s;
		}else if(strcmp(dictionary[i].key,"guid")==0){
			item->guid=dictionary[i].val->val.s;
		}
	}
	
	return item;
}

/** The function from as3_t2. Reads the contents of the file to struct rsschannel *rss */
struct rsschannel* readFileToRSS(char* filename){
	FILE *fp;
	int size;
	
	fp = fopen(filename, "r");
	
	if (fp == NULL){
		printf("File %s doesn't exists!\n", filename);
		exit(0);
	}
	
	// seek to end of file
	fseek(fp, 0, SEEK_END); 
	// get current file pointer
	size = ftell(fp)/sizeof(char); 
	// seek back to beginning of file
	fseek(fp, 0, SEEK_SET); 
	
	char buffer[size];
	be_node* channel;
	struct rsschannel *rss;
	int i;
	
    // Read the file
	fgets(buffer, size, fp);
    
    // Decode the contents
	channel = be_decode(buffer);
	rss = malloc(sizeof(struct rsschannel));

	// check the channel to be of type dictionary
	if (channel->type!=BE_DICT) {
		printf("Channel needs to be dictionary!"); 
		exit(0); 
	}

	rss->itemCount = 0;
	rss->items = NULL;

	for (i = 0; channel->val.d[i].val; ++i) {
		if(strcmp(channel->val.d[i].key, "title")==0){
			rss->title = channel->val.d[i].val->val.s;
		} else if(strcmp(channel->val.d[i].key, "link")==0){
			rss->link = channel->val.d[i].val->val.s;
		} else if(strcmp(channel->val.d[i].key, "description")==0){
			rss->description = channel->val.d[i].val->val.s;
		} else if(strcmp(channel->val.d[i].key, "language")==0){
			rss->language = channel->val.d[i].val->val.s;
		} else if(strncmp(channel->val.d[i].key, "item", 4)==0){
			rss->itemCount++;
			// realloc the items list
			rss->items = realloc(rss->items, rss->itemCount * sizeof(rssitem*));
			// parse the item and place it in the list of items
			rss->items[rss->itemCount - 1] = parseItem(channel->val.d[i].val->val.d);
		}
	}
    // Close the file
	fclose(fp);
	
	return rss;
}

/** Create a file and add it to the root directory */
static void createFile(int index, char* name, char* data){
    struct inode_stat *file_stat;
    file_stat = malloc(sizeof(struct inode_stat));
    
    file_stat->mode = S_IFREG | 0444;
    file_stat->uid = 0;
    file_stat->gid = 0;
    file_stat->size = 0;
    file_stat->dev = NO_DEV;
    files[index] = add_inode(get_root_inode(), name, NO_INDEX, file_stat, 0, (cbdata_t) data);
}

/** Hook called when vtreeFS is initializing */
static void my_init_hook(void){       
    char *content, *name;
    int i, length;
    
    content = NULL;
    name = NULL;
    
    // Read the file, parse the file and save it to static struct rss
    rss = readFileToRSS(FILE_LOCATION);
    
    // Allocate memory for the files array storage
    files = malloc(rss->itemCount*sizeof(struct inode*));
    
    
    for (i = 0; i<rss->itemCount; i++) {
        // Calculate the number of bytes to allocate for the content of the file
        length = strlen(rss->items[i]->title) + 
            strlen(rss->items[i]->link) + 
            strlen(rss->items[i]->pubDate) + 
            strlen(rss->items[i]->guid) + 
            strlen(rss->items[i]->description) + 64;
        
        // Allocate space for the content and name of the file
        content = malloc(length*sizeof(char));
        name = malloc(24*sizeof(char));
        
        // Generate the content of the file
        sprintf(content, "title=%s\nlink=%s\npubDate=%s\nguid=%s\ndescription=%s", rss->items[i]->title, rss->items[i]->link, rss->items[i]->pubDate, rss->items[i]->guid, rss->items[i]->description);  
        // Generate the name of the file
        sprintf(name, "item-%d", i);
        // Create the file and add it to the root directory
        createFile(i, name, content);
    }
    
}

/** Hook called when you read a file */
static int my_read_hook(struct inode *inode, off_t offset, char **ptr, size_t *len, cbdata_t cbdata){
    // cbdata is actually the data stored in the file, so use that as content to return
    char *content = (char*)cbdata;
    // Calculate the length of the content
    int lenght = strlen(content);
    
    // Reallocate memory if needed
    data = realloc(data, sizeof(char)*lenght);
    
    strcpy(data, content);
    
   /* If the offset is beyond the end of the string, return EOF. */
    if (offset > strlen(data)) {
            *len = 0;

            return OK;
    }
    
    

    /* Otherwise, return a pointer into 'data'. If necessary, bound the
     * returned length to the length of the rest of the string. Note that
     * 'data' has to be static, because it will be used after this function
     * returns.
     */
    *ptr = data + offset;

    if (*len > strlen(data) - offset)
            *len = strlen(data) - offset;

    return OK;
}

/* The table with callback hooks. */
struct fs_hooks my_hooks = {
        my_init_hook,
        NULL, /* cleanup_hook */
        NULL, /* lookup_hook */
        NULL, /* getdents_hook */
        my_read_hook,
        NULL, /* rdlink_hook */
        NULL  /* message_hook */
};

int main(void){
    struct inode_stat root_stat;
    
    data = NULL;

    /* Make the root directory */
    root_stat.mode = S_IFDIR | 0555;
    root_stat.uid = 0;
    root_stat.gid = 0;
    root_stat.size = 0;
    root_stat.dev = NO_DEV;

    /* Now start VTreeFS. Preallocate 10 inodes, which is more than we'll
     * need for this example. No indexed entries are used.
     */
    start_vtreefs(&my_hooks, 10, &root_stat, 0);

    /* The call above never returns. This just keeps the compiler happy. */
    return 0;
}